﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Kaskelot.SP.UI.Analys.Domain;
using Kaskelot.SP.UI.Analys.ViewModel;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.Data.DataFilter;
using Telerik.Windows.Data;
using Kaskelot.SP.UI.Analys.Data;
using System.Collections.ObjectModel;

namespace Kaskelot.SP.UI.Analys.Controls
{
	public partial class FilterBox : UserControl
	{
		private List<Entity> currentTypesInFilters;
		private List<LookupField> lookupInfo;

		public FilterBox()
		{
			InitializeComponent();
			currentTypesInFilters = new List<Entity>();
			Loaded += new RoutedEventHandler(FilterBox_Loaded);
		}

		public List<ViewModel.IFilterPart> GetCurrentFilters()
		{
			List<IFilterPart> parts = new List<IFilterPart>();

			if (Filter.FilterDescriptors.Count > 1)
			{
				FilterGroup group = new FilterGroup();
				group.Operator = Filter.FilterDescriptors.LogicalOperator == FilterCompositionLogicalOperator.And ?
					ViewModel.GroupOperator.And : ViewModel.GroupOperator.Or;
				group.Parts = new List<IFilterPart>();
				parts.Add(group);

				foreach (var item in Filter.FilterDescriptors)
				{
					AddPartsRecursive(group.Parts, item);
				}
			}
			else if (Filter.FilterDescriptors.Count == 1)
			{
				AddPartsRecursive(parts, Filter.FilterDescriptors.First());
			}

			return parts;
		}

		public void FilterOperatorsLoading(object sender, FilterOperatorsLoadingEventArgs args)
		{
			if(args.AvailableOperators.Contains(Telerik.Windows.Data.FilterOperator.IsNotContainedIn))
				args.AvailableOperators.Remove(Telerik.Windows.Data.FilterOperator.IsNotContainedIn);

			if (args.AvailableOperators.Contains(Telerik.Windows.Data.FilterOperator.IsContainedIn))
				args.AvailableOperators.Remove(Telerik.Windows.Data.FilterOperator.IsContainedIn);
		}

		public void SetFilters(List<IFilterPart> filters)
		{
			SetFiltersRecursive(filters, Filter.FilterDescriptors);
		}

		private void SetFiltersRecursive(List<IFilterPart> filters, FilterDescriptorCollection descriptors)
		{
			descriptors.Clear();
			filters.ForEach(delegate(IFilterPart filterPart)
			{
				if (filterPart is FieldFilter)
				{
					var f = filterPart as FieldFilter;
					if (lookupInfo.Any(li => li.FieldName == f.Field.LookupColumn))
					{
						LookupField lookup = lookupInfo.First(li => li.FieldName == f.Field.LookupColumn);
						descriptors.Add(new FilterDescriptor
						{
							Member = f.Field.LookupColumn,
							Operator = (Telerik.Windows.Data.FilterOperator)f.Operator,
							Value = lookup.Values.First(v => v.ID == (Guid)f.Value).Value
						});
					}
					else
					{
						descriptors.Add(new FilterDescriptor
						{
							Member = f.Field.InternalName,
							Operator = (Telerik.Windows.Data.FilterOperator)f.Operator,
							Value = f.Value
						});
					}
				}
				else if (filterPart is FilterGroup)
				{
					var f = filterPart as FilterGroup;
					CompositeFilterDescriptor newCompFilter = new CompositeFilterDescriptor
					{
						LogicalOperator = f.Operator == GroupOperator.And ? FilterCompositionLogicalOperator.And : FilterCompositionLogicalOperator.Or
					};
					SetFiltersRecursive(f.Parts, newCompFilter.FilterDescriptors);
					descriptors.Add(newCompFilter);
				}
			});
		}

		private void AddPartsRecursive(List<IFilterPart> parts, IFilterDescriptor item)
		{
			if (item is FilterDescriptor)
			{
				var descr = item as FilterDescriptor;

				if (lookupInfo.Any(l => l.FieldName == descr.Member))
				{
					var lookupThingy = lookupInfo.First(f => f.FieldName == descr.Member);

					parts.Add(new FieldFilter
					{
						Field = GetFieldByName(descr),
						Operator = (ViewModel.FilterOperator)descr.Operator,
						Value = lookupThingy.Values.First(v => v.Value.Equals(descr.Value)).ID
					});
				}
				else
				{
					parts.Add(new FieldFilter
					{
						Field = GetFieldByName(descr),
						Operator = (ViewModel.FilterOperator)descr.Operator,
						Value = descr.Value
					});
				}
			}
			else if (item is CompositeFilterDescriptor)
			{
				var composite = item as CompositeFilterDescriptor;
				FilterGroup group = new FilterGroup();
				group.Operator = composite.LogicalOperator == FilterCompositionLogicalOperator.And ? GroupOperator.And : GroupOperator.Or;
				group.Parts = new List<IFilterPart>();
				parts.Add(group);


				foreach (var child in composite.FilterDescriptors)
				{
					AddPartsRecursive(group.Parts, child);
				}
			}
		}

		private Field GetFieldByName(FilterDescriptor descr)
		{
			foreach (var entity in currentTypesInFilters)
			{
				foreach (var field in entity.Fields)
				{
					if (field.LookupColumn == descr.Member || field.InternalName == descr.Member)
						return field;
				}
			}
			return null;
		}

		public void SetDataSource(List<Entity> types)
		{
			types.Where(t => !currentTypesInFilters.Any(f => f.Name == t.Name)).ToList().ForEach(delegate(Entity currType)
			{
				Filter.ItemPropertyDefinitions.AddRange(currType.Fields.Select(f => new ItemPropertyDefinition
				{
					DisplayName = currType.Name + " - " + f.NameInQueryBuilder,
					PropertyName = string.IsNullOrEmpty(f.LookupColumn) ? f.InternalName : f.LookupColumn,
					PropertyType = string.IsNullOrEmpty(f.LookupColumn) ? f.DataType : typeof(string)
				}));

				currentTypesInFilters.Add(currType);
			});

			List<Entity> removedEntities = currentTypesInFilters.Where(curr => !types.Any(t => t.Name == curr.Name)).ToList();
			removedEntities.ForEach(delegate(Entity type)
			{
				List<ItemPropertyDefinition> filtersToRemove = Filter.ItemPropertyDefinitions.Where(prop => prop.DisplayName.StartsWith(type.Name + " -")).ToList();
				filtersToRemove.ForEach(delegate(ItemPropertyDefinition prop)
				{
					Filter.ItemPropertyDefinitions.Remove(prop);
				});
				currentTypesInFilters.Remove(type);
			});
		}

		public void Reset()
		{
			Filter.FilterDescriptors.Clear();
		}

		void FilterBox_Loaded(object sender, RoutedEventArgs e)
		{
			this.PaneGroup.Visibility = System.Windows.Visibility.Collapsed;
			AnalyseServiceWrapper.Instance.GetLookupInformation(
				(List<LookupField> result) =>
				{
					lookupInfo = result;

					lookupInfo.ForEach((LookupField field) =>
						{
							EditorTemplateSelector.EditorTemplateRules.Add(new FilterComponents.EditorTemplateRule
								{
									PropertyName = field.FieldName,
									DataTemplate = (DataTemplate)Resources["ComboBoxEditor"]
								});
						});

					this.PaneGroup.Visibility = System.Windows.Visibility.Collapsed;
				},
				(Exception ex) =>
				{
					ErrorHandler.Instance.HandleError(ex);
				});
		}

		private void Filter_EditorCreated(object sender, EditorCreatedEventArgs e)
		{
			if (lookupInfo != null)
			{
				var foundField = lookupInfo.FirstOrDefault(item => item.FieldName == e.ItemPropertyDefinition.PropertyName);
				if (foundField != null)
				{
					if (((RadComboBox)e.Editor).ItemsSource == null)
						((RadComboBox)e.Editor).ItemsSource = foundField.Values.Select(v => v.Value);
				}
			}

			LayoutRoot.InvalidateArrange();
			LayoutRoot.InvalidateMeasure();
			LayoutRoot.UpdateLayout();

			Filter.InvalidateArrange();
			Filter.InvalidateMeasure();
			Filter.UpdateLayout();
		}

		private void ToggleFiltersButton_Click(object sender, RoutedEventArgs e)
		{
			if (this.PaneGroup.Visibility == System.Windows.Visibility.Collapsed)
			{
				this.PaneGroup.Visibility = System.Windows.Visibility.Visible;
				this.ExpandImage.Visibility = System.Windows.Visibility.Collapsed;
				this.CollapseImage.Visibility = System.Windows.Visibility.Visible;
			}
			else
			{
				this.PaneGroup.Visibility = System.Windows.Visibility.Collapsed;
				this.ExpandImage.Visibility = System.Windows.Visibility.Visible;
				this.CollapseImage.Visibility = System.Windows.Visibility.Collapsed;
			}
		}
	}
}
